#include "InvocationList.h"

#include "../Threading/AutoLock.h"
#include "MethodInvoker.h"

#include <cassert>

namespace xgu{

Lock InvocationList::global_lock_;
Thread::TLS InvocationList::invoke_queue_tls_(TLS_OUT_OF_INDEXES);

InvocationList::InvocationList(void) : tid_(Thread::getTid())
{
	if (invoke_queue_tls_ == TLS_OUT_OF_INDEXES)
	{
		AutoLock autolock(global_lock_);
		if (invoke_queue_tls_ == TLS_OUT_OF_INDEXES)
		{
			invoke_queue_tls_ = Thread::TlsAlloc();
			assert(invoke_queue_tls_ != TLS_OUT_OF_INDEXES);
			Thread::TlsSet(invoke_queue_tls_, this);
		}
	}
}

InvocationList::~InvocationList(void)
{
	assert(empty());
}

void InvocationList::DoEvents()
{
	InvocationList* il = reinterpret_cast<InvocationList*>(Thread::TlsGet(invoke_queue_tls_));
	if (il != NULL)
	{
		for (iterator iter = il->begin(); iter != il->end(); ++iter)
		{
			assert(*iter != NULL);
			Invokable* invokable = *iter;
			invokable->DoInvocations();
		}
	}
}

void InvocationList::Register( Invokable& invokable )
{
	InvocationList* il = NULL;

	{
		AutoLock autolock(global_lock_);
		if (invoke_queue_tls_ == TLS_OUT_OF_INDEXES)
		{
			il = new InvocationList();
			assert(il != NULL);
			invoke_queue_tls_ = Thread::TlsAlloc();
			Thread::TlsSet(invoke_queue_tls_, il);
		}
		else
		{
			il = reinterpret_cast<InvocationList*>(Thread::TlsGet(invoke_queue_tls_));
		}
	}

	assert(il != NULL);

	il->push_back(&invokable);
}

void InvocationList::Unregister( const Invokable& invokable )
{
	assert(invoke_queue_tls_ != TLS_OUT_OF_INDEXES);
	InvocationList* il = reinterpret_cast<InvocationList*>(Thread::TlsGet(invoke_queue_tls_));
	assert(il != NULL);

	for (iterator iter = il->begin(); iter != il->end(); ++iter)
	{
		assert(*iter != NULL);
		if (*iter == &invokable)
		{
			il->erase(iter);
			break;
		}
	}

	if (il->empty())
	{
		delete il;
		Thread::TlsSet(invoke_queue_tls_, NULL);
	}
}

}
